
#import "WWTCLInterp.h"
#import "WWVarView.h"
#import <ctype.h>

@implementation WWVarView

// the idea here is that this view has a tcl variable name associated
// with it.  When a corresponding WWVarValueView is dragged over it,
// it accepts the drag, sets its image to the contents of the
// WWVarValueView and sets the value of it's tcl variable to the

+ initialize { [WWVarView setVersion:3]; return self; }


- initFrame:(const NXRect *)frameRect
{
   const char  *dragTypes[] = { NXAsciiPboardType, NULL};


   [super initFrame:frameRect];
   [self registerForDraggedTypes:dragTypes count:1];

   tclVarNameSize = 32;
   tclVarName = (char *)NXZoneCalloc([self zone], tclVarNameSize, sizeof(char));
   tclVarValueSize = 32;
   tclVarValue = (char *)NXZoneCalloc([self zone], tclVarValueSize, sizeof(char));
   tclVarTypeSize = 32;
   tclVarType = (char *)NXZoneCalloc([self zone], tclVarTypeSize, sizeof(char));
   // <null pointer> is around 15 characters, so add (say) 40 bytes to cmdBuf...
   cmdBuf = (char *)NXZoneCalloc([self zone], (40 + tclVarNameSize + tclVarValueSize), sizeof(char));
   framed = NO;
   restrictDropToSameName = NO;
   restrictDropToSameType = NO;
   actAsSource = YES;
   actAsSink = YES;
   frameColor = NX_COLORBLACK;
   frameWidth = 3.0;
   nameSwitchView = nil;
   valueSwitchView = nil;
   typeSwitchView = nil;

   return self;
}

- awake
{
   const char  *dragTypes[] = { NXAsciiPboardType, NULL};


   [super awake];
   [self registerForDraggedTypes:dragTypes count:1];

   framed = NO;
   cmdBuf = (char *)NXZoneCalloc([self zone], (40 + tclVarNameSize + tclVarValueSize), sizeof(char));
   
   return self;
}

- free
{
   const char *fileType[] = { "" };
   Pasteboard *pboard = [Pasteboard newName:NXDragPboard];


   [pboard declareTypes:fileType num:0 owner:nil];
   [self unregisterDraggedTypes];
   image = nil; // don't free the potentially shared image
   if (tclVarName) { free(tclVarName); }
   if (tclVarValue) { free(tclVarValue); }
   if (tclVarType) { free(tclVarType); }
   if (cmdBuf) { NXZoneFree([self zone], cmdBuf); }

   return [super free];
}

- reinitializeStringsFromZone:(NXZone *)zone
{
  tclVarName = (char *)NXZoneCalloc(zone, tclVarNameSize, sizeof(char));
  tclVarValue = (char *)NXZoneCalloc(zone, tclVarValueSize, sizeof(char));
  tclVarType = (char *)NXZoneCalloc(zone, tclVarTypeSize, sizeof(char));
  cmdBuf = (char *)NXZoneCalloc([self zone], (40 + tclVarNameSize + tclVarValueSize), sizeof(char));
  return self;
}

- copyFromZone:(NXZone *)zone
{
  id newCopy = [super copyFromZone:zone];

  [newCopy reinitializeStringsFromZone:zone];
  [newCopy setTclVarName:tclVarName];
  [newCopy setTclVarValue:tclVarValue];
  [newCopy setTclVarType:tclVarType];

  return newCopy;
}

- drawSelf:(const NXRect *)rects :(int)num
{
  [super drawSelf:rects :num];
  if (framed)
  {  NXSetColor(frameColor);
     NXFrameRectWithWidth(&bounds, (NXCoord)frameWidth);
  }
  return self;
}

/*-------------------- methods to be the source of a dragging operation */

- mouseDown:(NXEvent *)theEvent
{
   NXPoint  zero = {0.0, 0.0};
   id       thePboard;
   const char  *dragTypes[] = { NXAsciiPboardType, NULL};
   static char *fooBarBaz = "fooBarBaz";

	
   if (!image) { return self; }
   if (!actAsSource) { return self; }

   // Prevent default window ordering (this is a new appkit feature)
   [NXApp preventWindowOrdering];

   //get the Pboard
   thePboard = [Pasteboard newName:NXDragPboard];
   [thePboard declareTypes:dragTypes num:1 owner:nil];

   // we're not really going to use this info, we're just sticking something on the board...
   [thePboard writeType:NXAsciiPboardType data:fooBarBaz length:(1 + strlen(fooBarBaz))];
    
   //start the drag
   [self dragImage:image	// visible on screen during drag
                at:&zero	// offset the mouse point for the drag
            offset:&zero	// offset the inital mouse pt
             event:theEvent	// theEvent structure
        pasteboard:thePboard	// a Pasteboard with data on it
            source:self		// source object
         slideBack:YES];	// if no destination animate back to source
	
    return self;
}

- draggedImage:(NXImage *)image beganAt:(NXPoint *)screenPoint  { return self; }

- (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag  { return (NX_DragOperationCopy | NX_DragOperationGeneric); }

- draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint deposited:(BOOL)flag { return self; }

/*-------------------- methods to be the destination of a dragging operation */

- (NXDragOperation)draggingEntered:sender
{
   if (!actAsSink)
   {  if (framed)
      {  framed = NO;
	 [self display];
      }
      return NX_DragOperationNone;
   }

   // check to make sure we are not the source and this operation is a copy
   if (   ([sender draggingSourceOperationMask] & NX_DragOperationCopy) 
       && ([sender draggingSource] != self)
       && ([[sender draggingSource] respondsTo:@selector(tclVarValue)]))
   {  
      // if we're selective, need to check the var's name
      // if it's not the same as ours, we're outta here
      if (restrictDropToSameName)
      {  if (tclVarName && [[sender draggingSource] tclVarName] && strcmp(tclVarName, [[sender draggingSource] tclVarName]))
         {  if (framed)
	    {  framed = NO;
	       [self display];
 	    }
            return NX_DragOperationNone;
	 }
      }
      if (restrictDropToSameType)
      {  if (tclVarType && [[sender draggingSource] tclVarType] && strcmp(tclVarType, [[sender draggingSource] tclVarType]))
         {  if (framed)
	    {  framed = NO;
	       [self display];
 	    }
            return NX_DragOperationNone;
	 }
      }
      framed = YES;
      [self display];
      //return the type of operation we want to do, this will dictate
      //the type of cursor will show up when accepting the drag
      return NX_DragOperationCopy;
   }
   if (framed) 
   {  framed = NO;
      [self display];
   }
   return NX_DragOperationNone;
}

- (NXDragOperation)draggingUpdated:sender
{
   if (!actAsSink)
   {  if (framed)
      {  framed = NO;
	 [self display];
      }
      return NX_DragOperationNone;
   }

   // make sure that this is a copy operation, and that this instance
   // of DragView is not the source
   if (   ([sender draggingSourceOperationMask] & NX_DragOperationCopy) 
       && ([sender draggingSource] != self)
       && ([[sender draggingSource] respondsTo:@selector(tclVarValue)]))
    { 
      if (restrictDropToSameName)
      {  if (tclVarName && [[sender draggingSource] tclVarName] && strcmp(tclVarName, [[sender draggingSource] tclVarName]))
         {  if (framed)
	    {  framed = NO;
	       [self display];
 	    }
            return NX_DragOperationNone;
	 }
      }
      if (restrictDropToSameType)
      {  if (tclVarType && [[sender draggingSource] tclVarType] && strcmp(tclVarType, [[sender draggingSource] tclVarType]))
         {  if (framed)
	    {  framed = NO;
	       [self display];
 	    }
            return NX_DragOperationNone;
	 }
      }
      framed = YES;
      [self display];
      return NX_DragOperationCopy;
   }
   if (framed) 
   {  framed = NO;
      [self display];
   }
   return NX_DragOperationNone;
}

- draggingExited:sender
{
   if (!actAsSink)
   {  if (framed)
      {  framed = NO;
	 [self display];
      }
      return NX_DragOperationNone;
   }

   if (framed) 
   {  framed = NO;
      [self display];
   }
   return self;
}

- (BOOL)prepareForDragOperation:sender  
{  
   if (!actAsSink)
   {  if (framed)
      {  framed = NO;
	 [self display];
      }
      return NO;
   }
   return YES; 
}

static int notJustBlanks(const char *str)
{
  const char  *ptr;


  ptr = str;
  if (ptr)
  {  while (*ptr && isspace(*ptr))
     {  ptr++;
     }
     if (*ptr) // if we're not at the end and we stopped, it's not just blanks...
     {  return 1;
     }
  }
  return 0;
}


- (BOOL)performDragOperation:sender
{
   if (!actAsSink)
   {  if (framed)
      {  framed = NO;
	 [self display];
      }
      return NO;
   }

   // make sure that what we are getting is from the same application
   // and did not originate from this view.
   if (   [sender isDraggingSourceLocal] 
       && ([sender draggingSource] != self) 
       && ([[sender draggingSource] respondsTo:@selector(tclVarValue)]))

   {  // should check to make sure the sender will respond appropriately...
      if (restrictDropToSameName)
      {  if (tclVarName && [[sender draggingSource] tclVarName] && strcmp(tclVarName, [[sender draggingSource] tclVarName]))
         {  if (framed) 
	    {  framed = NO;
	       [self display];
	    }
	    return NO;
	 }
      }
      if (restrictDropToSameType)
      {  if (tclVarType && [[sender draggingSource] tclVarType] && strcmp(tclVarType, [[sender draggingSource] tclVarType]))
         {  if (framed) 
            {  framed = NO;
	       [self display];
	    }
	    return NO;
	 }
      }
      [self setImage:nil];
      [self setImage:[[sender draggingSource] image]];
      [self setTclVarValue:[[sender draggingSource] tclVarValue]];
      if (nameSwitchView)
      {  //[nameSwitchView showViewNamed:tclVarName];
      }
      if (typeSwitchView)
      {  //[typeSwitchView showViewNamed:tclVarType];
      }
      if (notJustBlanks(tclVarValue))
      {  // cmdBuf is basically "set <tclVarName> <tclVarValue>"
         if (valueSwitchView)
         {  //[valueSwitchView showViewNamed:tclVarValue];
         }
         sprintf(cmdBuf, "set %s %s", tclVarName, tclVarValue);
         [interp globalEval:cmdBuf];
      }
      framed = NO;
      [self display];
   }
   if (framed) 
   {  framed = NO;
      [self display];
   }
   return YES;
}

- concludeDragOperation:sender {  return self; }

/*-------------------- methods to display, and other useful stuff */

- (BOOL)acceptsFirstMouse  {  return YES; }

- (BOOL)shouldDelayWindowOrderingForEvent:(NXEvent *)theEvent {  return YES; }

// set and get stuff

- setTclVarValue:(const char *)str 
{ 
  int   cnt;


  if (tclVarValueSize <= 0) 
  {  tclVarValueSize = 32; 
     tclVarValue = (char *)NXZoneCalloc([self zone], tclVarValueSize, sizeof(char));
     cmdBuf = (char *)NXZoneCalloc([self zone], (40 + tclVarNameSize + tclVarValueSize), sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclVarValueSize)
  {  tclVarValueSize *= 2;
     tclVarValue = (char *)NXZoneRealloc([self zone], tclVarValue, tclVarValueSize);
  }
  cmdBuf = (char *)NXZoneRealloc([self zone], cmdBuf, (40 + tclVarNameSize + tclVarValueSize));
  strcpy(tclVarValue, str); 
  return self; 
}
//
- (const char *)tclVarValue { return tclVarValue; }


- setTclVarName:(const char *)str 
{ 
  int   cnt;


  if (tclVarNameSize <= 0) 
  {  tclVarNameSize = 32; 
     tclVarName = (char *)NXZoneCalloc([self zone], tclVarNameSize, sizeof(char));
     cmdBuf = (char *)NXZoneCalloc([self zone], (40 + tclVarNameSize + tclVarValueSize), sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclVarNameSize)
  {  tclVarNameSize *= 2;
     tclVarName = (char *)NXZoneRealloc([self zone], tclVarName, tclVarNameSize);
  }
  cmdBuf = (char *)NXZoneRealloc([self zone], cmdBuf, (40 + tclVarNameSize + tclVarValueSize));
  strcpy(tclVarName, str); 
  return self; 
}
//
- (const char *)tclVarName { return tclVarName; }

- setTclVarType:(const char *)str 
{ 
  int   cnt;


  if (tclVarTypeSize <= 0) 
  {  tclVarTypeSize = 32; 
     tclVarType = (char *)NXZoneCalloc([self zone], tclVarTypeSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclVarTypeSize)
  {  tclVarTypeSize *= 2;
     tclVarType = (char *)NXZoneRealloc([self zone], tclVarType, tclVarTypeSize);
  }
  strcpy(tclVarType, str); 
  return self; 
}
//
- (const char *)tclVarType { return tclVarType; }


- (BOOL)restrictDropToSameName { return restrictDropToSameName; }
- setRestrictDropToSameName:(BOOL)flag {  restrictDropToSameName = flag; return self; }

- (BOOL)restrictDropToSameType { return restrictDropToSameType; }
- setRestrictDropToSameType:(BOOL)flag {  restrictDropToSameType = flag; return self; }

- (BOOL)actAsSink { return actAsSink; }
- setActAsSink:(BOOL)flag 
{  if (actAsSink && !flag) // we're about to turn it off...
   {  [self unregisterDraggedTypes];
   }
   if (!actAsSink && flag) // we're about to turn it on...
   {  const char  *dragTypes[] = { NXAsciiPboardType, NULL};
      [self registerForDraggedTypes:dragTypes count:1];
   }
   
   actAsSink = flag; 
   return self; 
}

- (BOOL)actAsSource { return actAsSource; }
- setActAsSource:(BOOL)flag {  actAsSource = flag; return self; }

// IB stuff
- (const char *)getInspectorClassName 
{  
   NXEvent  *event = [NXApp currentEvent];

   if (event->flags & NX_ALTERNATEMASK)
   {  return [super getInspectorClassName];
   }
   
   return "WWVarViewIBInspector"; 
}

- write:(NXTypedStream *)stream 
{
   [super write:stream];
   NXWriteTypes(stream, "***cccfc", &tclVarName, &tclVarValue, &tclVarType, 
		&restrictDropToSameName, &restrictDropToSameType, &actAsSource, &frameWidth, &actAsSink);
   NXWriteColor(stream, frameColor);
   return self;
}
//
- read:(NXTypedStream *)stream 
{
   int version;

   [super read:stream];

   version = NXTypedStreamClassVersion(stream, "WWVarView");
   if (version == 1)
   {  NXReadTypes(stream, "***ccc", 
		  &tclVarName, &tclVarValue, &tclVarType, 
		  &restrictDropToSameName, &restrictDropToSameType, &actAsSource);
      frameColor = NX_COLORBLACK;
      if (tclVarName)
      {  tclVarNameSize = strlen(tclVarName) + 1;
      }
      else
      {  tclVarNameSize = 0;
      }
      if (tclVarValue)
      {  tclVarValueSize = strlen(tclVarValue) + 1;
      }
      else
      {  tclVarValueSize = 0;
      }
      if (tclVarType)
      {  tclVarTypeSize = strlen(tclVarType) + 1;
      }
      else
      {  tclVarTypeSize = 0;
      }
   }
   if (version == 2)
   {  NXReadTypes(stream, "***cccf", 
		  &tclVarName, &tclVarValue, &tclVarType, 
		  &restrictDropToSameName, &restrictDropToSameType, &actAsSource, &frameWidth);
      frameColor = NXReadColor(stream);

      if (tclVarName)
      {  tclVarNameSize = strlen(tclVarName) + 1;
      }
      else
      {  tclVarNameSize = 0;
      }
      if (tclVarValue)
      {  tclVarValueSize = strlen(tclVarValue) + 1;
      }
      else
      {  tclVarValueSize = 0;
      }
      if (tclVarType)
      {  tclVarTypeSize = strlen(tclVarType) + 1;
      }
      else
      {  tclVarTypeSize = 0;
      }
   }
   if (version == 3)
   {  NXReadTypes(stream, "***cccfc", 
		  &tclVarName, &tclVarValue, &tclVarType, 
		  &restrictDropToSameName, &restrictDropToSameType, &actAsSource, &frameWidth, &actAsSink);
      frameColor = NXReadColor(stream);

      if (tclVarName)
      {  tclVarNameSize = strlen(tclVarName) + 1;
      }
      else
      {  tclVarNameSize = 0;
      }
      if (tclVarValue)
      {  tclVarValueSize = strlen(tclVarValue) + 1;
      }
      else
      {  tclVarValueSize = 0;
      }
      if (tclVarType)
      {  tclVarTypeSize = strlen(tclVarType) + 1;
      }
      else
      {  tclVarTypeSize = 0;
      }
   }
   return self;
}


@end
